package club.qweqwe.dao.impl;

import club.qweqwe.dao.DAO;
import club.qweqwe.utils.ReflectUtils;
import org.apache.commons.beanutils.BeanUtils;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BasicDaoImpl<T> extends DAO<T> {
    protected DataSource dataSource = null;

    public BasicDaoImpl() {
    }

    public BasicDaoImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }


    @Override
    public int update(String sql, Object... objects) {
        try (Connection connection = dataSource.getConnection()) {
            try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
                for (int i = 0; i < objects.length; i++) {
                    preparedStatement.setObject(i + 1, objects[i]);
                }

                return preparedStatement.executeUpdate();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
    }

    @Override
    public T query(String sql, Object... objects) {
        try (Connection connection = dataSource.getConnection()) {
            try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
                for (int i = 0; i < objects.length; i++) {
                    preparedStatement.setObject(i + 1, objects[i]);
                }
                try (ResultSet resultSet = preparedStatement.executeQuery()) {
                    ResultSetMetaData metaData = resultSet.getMetaData();

                    {
                        int columnLen = metaData.getColumnCount();

                        Class clazz = ReflectUtils.getSuperGenericType(this.getClass());
                        Constructor constructor = clazz.getConstructor(null);
                        Object o = constructor.newInstance(null);
                        while (resultSet.next()) {
                            for (int i = 0; i < columnLen; i++) {
                                Object field = resultSet.getObject(i + 1);
                                BeanUtils.setProperty(o, metaData.getColumnLabel(i + 1), field);
                            }

                            return (T) o;
                        }
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int insert(String sql, Object... objects) {
        return update(sql, objects);
    }

    @Override
    public int delete(String sql, Object... objects) {
        return update(sql, objects);
    }

    @Override
    public List<T> querySet(String sql, Object... objects) {
        try (Connection connection = dataSource.getConnection()) {
            try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
                for (int i = 0; i < objects.length; i++) {
                    preparedStatement.setObject(i + 1, objects[i]);
                }
                try (ResultSet resultSet = preparedStatement.executeQuery()) {
                    ResultSetMetaData metaData = resultSet.getMetaData();

                    {
                        int columnLen = metaData.getColumnCount();
                        List<T> elems = new ArrayList<>();

                        Class clazz = ReflectUtils.getSuperGenericType(this.getClass());
                        Constructor constructor = clazz.getConstructor(null);
                        while (resultSet.next()) {
                            Object o = constructor.newInstance(null);
                            for (int i = 0; i < columnLen; i++) {
                                Object field = resultSet.getObject(i + 1);
                                BeanUtils.setProperty(o, metaData.getColumnLabel(i + 1), field);
                            }
                            elems.add((T) o);
                        }
                        return elems;
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return null;
    }
}
